
local jass = require 'jass.common'
local japi = require 'jass.japi'
local slk  = require 'jass.slk'
local dbg  = require 'jass.debug'
local hook = require 'jass.hook'
local code = require 'jass.code'
local ai   = require 'jass.ai'

local handlelib = require 'util.handle'

local destructable = { }
setmetatable(destructable, destructable)
ac.destructable = destructable

destructable.modify_flag = false

--- 存放所有可破坏物
destructable.alldestructables = { }

--- 存放预设可破坏物
destructable.globals = { }

--- 输出
function destructable:__tostring()
    return ("可破坏物[%d]"):format(self.handle or 0)
end 

--- handle检查
--- @params handle 
--- @return boolean 是否是可以用的handle
function destructable.handle_check(handle)
    if not handle or handle == 0 then 
        return false 
    end 
    if jass.GetDestructableTypeId(handle) == 0 then 
        return false
    end
    return true
end

--- 转换为可破坏物
function destructable.j_destructable(handle)
    --- 先判定是否可用
    if not destructable.handle_check(handle) then 
        return 
    end
    --- 如果没有则进行转换
    if not destructable.alldestructables[handle] then 
        destructable.convert_handle(handle)
    end 
    return destructable.alldestructables[handle] 
end 

--- 获取对象 (可以异步使用,不会自动转换)
--- @params handle 
function destructable:__call(handle)
    if destructable.handle_check(handle) then 
        return destructable.alldestructables[handle] 
    end
end 

--- 转换
function destructable.convert_handle(handle)

    --- 获得对象
    local u = destructable.init_handle(handle)

    u._id = FourCC( jass.GetDestructableTypeId(handle) )

    u:init()
    
    return u
end 
 

--- 根据handle生成对象
function destructable.init_handle(handle)
    
    local u = setmetatable({}, destructable)

    --- 将handle 跟 对象绑定
    u.handle = handle 

    --- 加入全局表
    destructable.alldestructables[handle] = u

    --- 设置gchash值 防止异步
    u.gchash = handle 
    dbg.gchash(u,handle)

    --- 句柄引用
    dbg.handle_ref(handle)
      
    return u
end 

--- 创建
function destructable.create(id, where, facing, scale)

    local x = where and where.x or 0  
    local y = where and where.y or 0  

    local handle = jass.CreateDestructable( FourCC(id), x, y, facing or 270, scale or 1, 0)
    
    --- 创建失败
    if handle == 0 then  
        log.error('创建可破坏物失败', id) 
        return 
	end

    --- 获得对象
    local u = destructable.init_handle(handle) 

    u._id = id 
    --- 初始化
    u:init()

    u:notify('可破坏物-创建',u)
    return u 
end

--- 结构
local mt = {}
destructable.__index = mt 
setmetatable(mt, ac.widget)

--- 类型
mt.type = 'destructable'

--- 句柄
mt.handle = jass.ConvertUnitState(0)

--- 初始化
function mt:init()

    local id = self._id 

    self.effects = {}

    jass.TriggerRegisterDeathEvent(destructable.trigger.handle, self.handle)

    self:notify('可破坏物-初始化',self)

    return self
end 

--- 删除
function mt:remove()

    if self.removed then return end 

    self:notify('可破坏物-即将移除',self)
    self.removed = true

    local handle = self.handle 

    if self._creation then 
        destructable.globals[self._creation] = nil
    end  

    --移除计时器
    if self._timers then
        for i,t in ipairs(self._timers) do
            t:remove()
        end
        self._timers = nil 
    end
    
    --移除触发器
    if self._events then
        for name, event in pairs(self._events) do
            for i,trg in ipairs(event) do
                trg:remove()
                event[i] = nil
            end
            event:remove()
        end
        self.event = nil
    end

    --- 删除句柄
    jass.RemoveDestructable(handle)
    --- 解除引用
    dbg.handle_unref(handle)

    --- 将从全局表里面移除
    destructable.alldestructables[handle] = nil

    return true 
end 

--- 派发全局事件(有返回值)
--- @param name string
--- @param ... any
--- @return function
function mt:dispatch(name, ...)
	local res = ac.dispatch(self, name, ...)
	if res ~= nil then 
		return res 
	end 
    return ac.dispatch(ac.game,name,...)
end

--- 派发全局事件
--- @param name string
--- @param ... any
--- @return function
function mt:notify(name, ...)
	ac.notify(self, name, ...)
    ac.notify(ac.game,name,...)
end

--- 注册全局事件
--- @param name string
--- @return function
function mt:event(name)
	return ac.register(self, name)
end

--- 拓展接口名
mt.on = mt.event 
mt.register = mt.event
mt.wait = ac.uwait
mt.loop = ac.uloop
mt.timer = ac.utimer

--- 获取所有者
--- @return player 
function mt:get_owner() 
    return ac.player(16)
end

--获取可破坏物是否存活
function mt:is_alive()
    return jass.GetDestructableLife(self.handle) > 0
end

--播放可破坏物动画
function mt:play_animation(name)
    jass.SetDestructableAnimation(self.handle,name)
end 

--可破坏物是否无敌
function mt:is_god()
    return jass.IsDestructableInvulnerable(self.handle)
end

--设置可破坏物无敌状态
function mt:set_god(flag)
    jass.SetDestructableInvulnerable(self.handle, flag)
end

--获取生命值
function mt:get_life()
    return jass.GetDestructableLife(self.handle)
end

--设置生命值
function mt:set_life(life)
    local life_max = self:get_life_max()
    if life > life_max then
        life = life_max
    end
    jass.SetDestructableLife(self.handle , life)
end

--获取生命上限
function mt:get_life_max()
    return jass.GetDestructableMaxLife(self.handle)
end

--设置生命上限
function mt:set_life_max(life_max)
    jass.SetDestructableMaxLife(self.handle , life_max )
end

--设置显示
function mt:set_visible(bool)
    jass.ShowDestructable(self.handle, bool)
end

--获取ID
function mt:get_id()
    return self._id 
end 

local targTypes = {}

--获取作为目标的类型
function mt:get_target_types()
    local name = self:get_id()
    if not targTypes[name] then 
        local info = { }
        for typename in (self:get_slk("targType", "")):gmatch("[^%,]+") do 
            info[typename] = true
        end 
        targTypes[name] = info
    end 
    return targTypes[name]
end

--是树木
function mt:is_tree()
    return self:get_target_types()['tree']
end

--是残骸
function mt:is_debris()
    return self:get_target_types()['debris']
end

--是墙体
function mt:is_wall()
    return self:get_target_types()['wall']
end

--获取所在点
function mt:get_point()
    return ac.point(jass.GetDestructableX(self.handle),jass.GetDestructableY(self.handle))
end

--杀死可破坏物
function mt:kill()
    if self.removed then return end 
    jass.KillDestructable(self.handle)
end

--复活可破坏物
function mt:revive()
    if self.removed then return end
    jass.DestructableRestoreLife(self.handle, self:get_life_max(), false)
    self:play_animation('stand')
    self:notify('可破坏物-复活', self )
end

--大门状态常量
local modify = {}
modify.OPEN = 1
modify.CLOSE = 0
modify.DESTROY = -1

--设置可破坏物状态
function mt:set_modify(name)
    if self.removed then return end
    local state = modify[name]
    if not state then return end
    if state == 0 then
        if not self:is_alive() then
            self:revive()
        end
        self.state = state
        self:play_animation('stand')
        self:notify('大门-打开', self )
    elseif state == 1 then
        if self:is_alive() then
            destructable.modify_flag = true
            self:kill()
            destructable.modify_flag = false
        end
        self.state = state
        self:play_animation('death alternate')
        self:notify('大门-关闭', self )
    elseif state == -1 then
        if self:is_alive() then
            self:kill()
        end
        self.state = state
        self:play_animation('death')
    end
end

--  获取可破坏物状态
function mt:get_modify()
    if self.state == 1 then 
        return "OPEN"
    elseif self.state == 0 then 
        return "CLOSE"
    elseif self.state == -1 then 
        return "DESTROY"
    end 
end 


--- 模块初始化
function destructable.init()
    local trigger = {}
    destructable.trigger = trigger
    trigger.action = function()
        local triggerdest = destructable.j_destructable(jass.GetTriggerWidget())
        if triggerdest and (not destructable.modify_flag == true) then 
            triggerdest:notify('可破坏物-死亡', triggerdest)
        end 
    end
    trigger.handle = ac.event.trigger()
    jass.TriggerAddAction(trigger.handle, trigger.action)
end

--- 更新回调
function destructable.update()

end

ac.game:on "游戏-生成可破坏物" (function ()

    if not destructable.doodadsdoo then 

        destructable.doodadsdoo = { }

        if not destructable.CreateAllDestructables then 

            destructable.CreateAllDestructables = code.CreateAllDestructables

            if not code.CreateAllDestructables then 
                print(" 创建 可破坏物的函数丢失", code.CreateAllDestructables )
            end 

            code.CreateAllDestructables = function()

            end

            local CreateDestructable = jass.CreateDestructable
            local CreateDestructableZ = jass.CreateDestructableZ
            local CreateDeadDestructable = jass.CreateDeadDestructable
            local CreateDeadDestructableZ = jass.CreateDeadDestructableZ

            code.GlobalDestructable = function(objectid, x, y, face, scale, variation ,creation)
                local handle = CreateDestructable(objectid, x, y, face, scale, variation)
                local object = destructable.j_destructable(handle)
                if object then 
                    object._creation = creation
                    destructable.globals[creation] = object
                    table.insert(destructable.doodadsdoo, object.handle)
                end 
                return handle 
            end 

            code.GlobalDestructableZ = function(objectid, x, y, z, face, scale, variation ,creation)
                local handle = CreateDestructableZ(objectid, x, y, z, face, scale, variation)
                local object = destructable.j_destructable(handle)
                if object then 
                    object._creation = creation
                    destructable.globals[creation] = object
                    table.insert(destructable.doodadsdoo, object.handle)
                end 
                return handle 
            end 

            code.GlobalDeadDestructable = function(objectid, x, y, face, scale, variation ,creation)
                local handle = CreateDeadDestructable(objectid, x, y, face, scale, variation)
                local object = destructable.j_destructable(handle)
                if object then 
                    object._creation = creation
                    destructable.globals[creation] = object
                    table.insert(destructable.doodadsdoo, object.handle)
                end 
                return handle 
            end 

            code.GlobalDeadDestructableZ = function(objectid, x, y, z, face, scale, variation ,creation)
                local handle = CreateDeadDestructableZ(objectid, x, y, z, face, scale, variation)
                local object = destructable.j_destructable(handle)
                if object then 
                    object._creation = creation
                    destructable.globals[creation] = object
                    table.insert(destructable.doodadsdoo, object.handle)
                end 
                return handle 
            end 

        end 

        if destructable.CreateAllDestructables then
            destructable.CreateAllDestructables()
        end
    else 
        -- 重新创建预设的可破坏物
        for i = #destructable.doodadsdoo, 1, -1 do 
            local handle = destructable.doodadsdoo[i]
            local object = destructable.alldestructables[handle]
            if object then 
                object:remove()
            end 
            destructable.doodadsdoo[i] = nil
        end 
        
        destructable.CreateAllDestructables()
    end 
end)

return destructable 